using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
namespace Lizk.SimpleHUD
{
#if !XBOX
[TypeConverter(typeof(FlagsEnumConverter))]
#endif
    [Flags]
    public enum Anchor
    {
        None = 0,
        Top = 1,
        Bottom = 2,
        Right = 4,
        Left = 8,
        All = Top | Bottom | Right | Left
    }

    public enum MovementPattern
    {
        Linear,
        DeltaBased
    }
    
    [Serializable]
    public abstract class HUDControl
    {
        protected bool visible = true;
        protected byte alpha = 255;
        public event EventHandler FadingStopped;
        protected RectangleS area = RectangleS.Empty;
        [NonSerialized]
        private byte alphaTarget = 0;
        private float fadeSpeed = 10f, fadeOffset = 0f;
        [NonSerialized]
        private bool isFading;
        private Anchor anchor = Anchor.Left | Anchor.Top;
        private MovementPattern movementPattern = MovementPattern.DeltaBased;

        public MovementPattern MovementPattern
        {
            get { return movementPattern; }
            set { movementPattern = value; }
        }

#if !XBOX
//        [TypeConverter(typeof(FlagsEnumConverter))]
#endif
        public Anchor Anchor
        {
            get { return anchor; }
            set { anchor = value; }
        }

        protected float minMoveLength = 10f;

        public float MinMoveLength
        {
            get { return minMoveLength; }
            set { minMoveLength = value; }
        }

        protected float minScaleDelta = 10f;

        public float MinScaleDelta
        {
            get { return minScaleDelta; }
            set { minScaleDelta = value; }
        }

        [NonSerialized]
        private object tag;

#if !XBOX
        [Browsable(false)]
#endif
        public object Tag
        {
            get { return tag; }
            set { tag = value; }
        }
        [NonSerialized]
        private object effectTag;

#if !XBOX
        [Browsable(false)]
#endif
        public object EffectTag
        {
            get { return effectTag; }
            set { effectTag = value; }
        }

        public T GetTag<T>()
        {
            return (T)tag;
        }

        public T GetEffectTag<T>()
        {
            return (T)effectTag;
        }

        public virtual void ResizeMove(RectangleS oldRect, RectangleS newRect)
        {
        
        }

#if !XBOX
        //[Browsable(false)]
#endif
        public RectangleS Bounds
        {
            get { return area; }
            set
            {
                RectangleS r = area;
                area = value;
                if (r != area)
                    ResizeMove(r, area);
                //                    Resized(this, null);
            }
        }

        public HUDContainer Parent { get; set; }

        public RectangleS AbsoluteBounds
        {
            get {
                if (Parent == null)
                    return this.Bounds;
                else
                    return Parent.AbsoluteBounds.Location + this.Bounds; 
            }
        }


        public SizeS Size
        {
            get { return area.Size; }
            set
            {
                RectangleS r = area;
                area.Size = value;
                if (r != area)
                    ResizeMove(r, area);
                //                    Resized(this, null);

            }
        }
        public PointS Location
        {
            get { return area.Location; }
            set
            {
                RectangleS r = area;
                area.Location = value;
                if (r != area)
                    ResizeMove(r, area);
                    //Resized(this, null);
            }
        }

#if !XBOX
        [Browsable(false)]
#endif
        public bool IsFading
        {
            get { return isFading; }
        }
        public virtual bool Visible
        {
            get { return visible; }
            set { visible = value; }
        }
        public int Alpha
        {
            get { return (int)alpha; }
            set { alpha = (byte)value; if (value <= 0) { alpha = 0; visible = false; } else visible = true; if (value > 255) alpha = (byte)255; }
        }
        public virtual void Fade(byte targetAlpha, float fadeSpeed)
        {
            if (targetAlpha < 0 || targetAlpha > 255 || fadeSpeed == 0)
                return;

            if (targetAlpha == alpha)
            {
                isFading = false;
                return;
            }

            alphaTarget = targetAlpha;

            this.fadeSpeed = fadeSpeed * 255;

            fadeOffset = 0f;
            isFading = true;

            if (targetAlpha > 0)
                visible = true;
        }
        protected void UpdateAlpha(TimeSpan ts)
        {
            if (!IsFading)
                return;

            if (alphaTarget == alpha)
            {
                if (alpha == 0)
                    visible = false;

                isFading = false;

                if (FadingStopped != null)
                    FadingStopped(this, null);
                return;
            }


            int direction = Math.Sign(alphaTarget - alpha);

            // set xNext
            fadeOffset += (float)ts.TotalSeconds * direction * fadeSpeed;

            int spacesMoved = (int)fadeOffset;

            if (spacesMoved != 0)
            {
                Alpha += spacesMoved;
                visible = true;

                fadeOffset = 0;

                if ((alpha - alphaTarget) * direction > 0)
                {
                    alpha = alphaTarget;
                }
            }
        }
        public virtual void Update(TimeSpan ts)
        {
            UpdateAlpha(ts);
            UpdateMovement(ts);
        }

        #region movement
        public event EventHandler MovementStopped;

        [NonSerialized]
        bool isMoving = false;

        public bool IsMoving { get { return isMoving; } }

        [NonSerialized]
        private PointS destination = PointS.Empty;
#if !XBOX
        [Browsable(false)]
#endif
        public PointS Destination
        {
            get { return destination; }
            set
            {
                destination = value;
                PointS pos = new PointS(Location.X, Location.Y);
                movementDirection = PointS.Normalize(destination - pos);
                if(movementDirection != PointS.Empty)
                    isMoving = true;
            }
        }
        [NonSerialized]
        private PointS movementDirection = PointS.Empty;

        [NonSerialized]
        private PointS destSize = PointS.Empty;
#if !XBOX
        [Browsable(false)]
#endif
        public PointS DestinationSize
        {
            get { return destSize; }
            set
            {
                destSize = value;
                PointS size = new PointS(Size.Width, Size.Height);
                sizeDelta = PointS.Normalize(destSize - size);
                isMoving = true;
            }
        }
        [NonSerialized]
        private PointS sizeDelta = PointS.Empty;

        private float movementSpeed = 6.2f;
        public float MovementSpeed { get { return movementSpeed; } set { movementSpeed = value; } }

        private float scaleSpeed = 6.2f;
        public float ScaleSpeed { get { return scaleSpeed; } set { scaleSpeed = value; } }

        public void UpdateMovement(TimeSpan ts)
        {
            if (!isMoving)
                return;

            // size
            PointS size = new PointS(Size.Width, Size.Height);
            PointS position = new PointS(Location.X, Location.Y);


            if (PointS.Length(destination - position) <= 1)
            {
                Location = new PointS(destination.X, destination.Y);
                destination = movementDirection = PointS.Empty;
            }

            if (movementDirection != PointS.Empty)
            {
                float length = movementPattern == MovementPattern.DeltaBased ? PointS.Length(destination - position) : 1;
                if (length < minMoveLength) length = minMoveLength;
                PointS newPos = position + (length * (float)ts.TotalSeconds * movementDirection * movementSpeed);

                if ((destination.X < newPos.X && destination.X > position.X) || (destination.X > newPos.X && destination.X < position.X))
                    newPos.X = destination.X;

                if ((destination.Y < newPos.Y && destination.Y > position.Y) || (destination.Y > newPos.Y && destination.Y < position.Y))
                    newPos.Y = destination.Y;

                Location = new PointS(newPos.X, newPos.Y);
            }

            if (PointS.Length(destSize - size) <= 1)
            {
                Size = new SizeS(destSize.X, destSize.Y);
                destSize = sizeDelta = PointS.Empty;
            }

            if (sizeDelta != PointS.Empty)
            {
                float length = movementPattern == MovementPattern.DeltaBased ? PointS.Length(destSize - size) : 1;
                if (length < minScaleDelta) length = minScaleDelta;
                PointS newSize = size + (length * (float)ts.TotalSeconds * sizeDelta * scaleSpeed);

                PointS newDelta = newSize - destSize ;

                if (newDelta.X * sizeDelta.X > 0)
                    newSize = destSize;


                if ((destSize.X < newSize.X && destSize.X > size.X) || (destSize.X > newSize.X && destSize.X < size.X))
                    newSize.X = destSize.X;

                if ((destSize.Y < newSize.Y && destSize.Y > size.Y) || (destSize.Y > newSize.Y && destSize.Y < size.Y))
                    newSize.Y = destSize.Y;

                Size = new SizeS(newSize.X, newSize.Y);
            }
            if (movementDirection == PointS.Empty && sizeDelta == PointS.Empty)
            {
                if (MovementStopped != null)
                    MovementStopped(this, null);
                isMoving = false;
            }

        }        
        #endregion
        [Flags]
        public enum MouseButtons { None = 1, Left = 2, Right = 4, Middle = 8}
        public class MouseEventArgs : EventArgs 
        {
            private PointS point;

            public PointS Point
            {
                get { return point; }
                set { point = value; }
            }
            private MouseButtons button;

            public MouseButtons Button
            {
                get { return button; }
                set { button = value; }
            }

            public MouseEventArgs(PointS point, MouseButtons button)
            {
                this.point = point;
                this.button = button;
            }
        }

        public virtual bool MouseDown(PointS point, MouseButtons buttons)
        {
            if(OnMouseDown != null)
                OnMouseDown(this, new MouseEventArgs(point, buttons));
            return true;
        }
        
        public virtual bool MouseUp(PointS point, MouseButtons buttons)
        {
            if (OnMouseUp != null)
                OnMouseUp(this, new MouseEventArgs(point, buttons));
            return true;
        }
        public event EventHandler<MouseEventArgs> OnMouseDown;
        public event EventHandler<MouseEventArgs> OnMouseUp;
        public event EventHandler<MouseEventArgs> OnMouseEnter;
        public event EventHandler<MouseEventArgs> OnMouseExit;

        public virtual void UpdateMousePosition(PointS position)
        {
            bool isover = Bounds.Contains(position);
            if (isover && !IsMouseOver)
            {
                isMouseOver = true;
                if(OnMouseEnter != null)
                    OnMouseEnter(this, new MouseEventArgs(position, MouseButtons.None));
            }else
            if (!isover && IsMouseOver)
            {
                isMouseOver = false;
                if (OnMouseExit != null)
                    OnMouseExit(this, new MouseEventArgs(position, MouseButtons.None));
            }
        }

        [NonSerialized]
        private bool isMouseOver = false;

#if !XBOX
        [Browsable(false)]
#endif
        public bool IsMouseOver
        {
            get { return isMouseOver; }
        }


    }

}
